package com.aop;

import cn.hutool.crypto.digest.DigestUtil;
import com.common.annotation.RepeatSubmit;
import com.exception.ApiException;
import com.util.IPUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author: cxt
 * @time: 2022/9/23
 */
@Aspect
@Component
@Slf4j
public class SubmitInspect {
    private final RedissonClient redissonClient;
    private StringRedisTemplate redisTemplate;

    private static final String PARAM_FLG = "SERVER:REPEAT_SUBMIT";
    private static final String TOKEN_KEY = "SERVER:TOKEN_KEY:%s";

    @Pointcut(value = "@annotation(com.common.annotation.RepeatSubmit)")
    public void controllerPointCut() {
    }

    @Around("controllerPointCut() && @annotation(submit)")
    public Object doAfterReturning(ProceedingJoinPoint joinPoint, RepeatSubmit submit) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String name = submit.localType().name();

        // 验证结果
        boolean res = false;
        // 参数验证
        if (RepeatSubmit.TYPE.PARAM.name().equals(name)) {
            String ipAdder = IPUtil.getIpAdder(request);
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();
            String className = method.getDeclaringClass().getName();
            // 加锁的key TODO，后续加上登录账号
            String key = PARAM_FLG + DigestUtil.md5Hex(String.format("%s-%s-%s", ipAdder, className, method));
            RLock lock = redissonClient.getLock(key);
            // 尝试加锁，最多等待0秒，上锁以后5秒自动解锁 [lockTime默认为5s, 可以自定义]
            res = lock.tryLock(0, submit.localTime(), TimeUnit.SECONDS);
        }

        // token验证
//        if (RepeatSubmit.TYPE.TOKEN.name().equals(name)) {
//            // 校验token是否存在,这里根据情况进行调整，Authorization
//            String token = request.getHeader("Authorization");
//            if (StringUtils.isBlank(token)) {
//                throw new ApiException(ApiException.E_UNAUTHORIZED);
//            }
//            // TODO，后续加上登录账号
//            String key = String.format(TOKEN_KEY, token);
//            res = Boolean.TRUE.equals(redisTemplate.delete(key));
//
//            // 删除失败，说明不存在
//        }

        if (!res) {
            throw new ApiException("提交过快，稍后重试！");
        }
        return joinPoint.proceed();
    }

    @Autowired
    public SubmitInspect(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Autowired
    public void setRedisTemplate(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
}
